#include "pch.h"
#include "List.h"

#include <memory.h>

#define _LIST_GROW_THRESHOLD_ 80
#define _LIST_SHRINK_THRESHOLD_ 60
#define _LIST_GROW_PERCENT_ 5
#define _MAX_GROW_SIZE 5
#define _MIN(x,y) ((x) < (y) ? (x) : (y))

#define _GROW_BY_PERCENT(x,p)  ((((x) * (p)) + (x)) / (p))

List::List(void)
{
	// The initial size of the Material Library is 20.
	this->size = _MAX_GROW_SIZE;

	// this is an empty list, so set the count to zero
	this->count = 0;

	// Allocate space to the library so it is ready to receive
	// new data.
	this->internalList = (void**)malloc(sizeof(void*) * size);
}


List::~List(void)
{
	free(this->internalList);

	this->size = 0;
	this->count = 0;
}

// Adds a new item to the list.
int List::Add(void* item)
{
	int countAfterAdd = this->count + 1;

	// test if the number of items inside the library is approaching 90%
	// of the library's internal buffer, if so reallocate more space to
	// fill the library.
	int percentageFilled =
		(countAfterAdd * 100) / this->size;

	if (percentageFilled > _LIST_GROW_THRESHOLD_)
	{
		// the new size should grow by n% or by flat size
		int newSize = _MIN( _GROW_BY_PERCENT(this->size, _LIST_GROW_PERCENT_), (this->size + _MAX_GROW_SIZE));
			
		this->internalList = (void**)realloc(this->internalList, newSize * sizeof(void*));

		// Set the new size of the material library
		this->size = newSize;
	}

	// Add the material to the library
	this->internalList[this->count] = item;

	// and store the index the material was added at so it can be returned.
	int index = this->count;

	// Set the new Count
	this->count = countAfterAdd;

	// return the index the material was added at.
	return index;
}

void List::Remove(void* item)
{
	int index = -1;

	// Do a scan search to find the item to remove in our dynamic array,
	// once the item is located record the index in the array the item 
	// was found.
	// keep looping until we reach the end of the dynamic array or until
	// we find the item in the array.
	for(int i = 0; i < this->count && index == -1; i ++)
	{
		if (this->internalList[i] == item)
		{
			index = i;		
		}
	}

	// if the index is not equal to -1, then we have found the item in the 
	// array to remove, prceed to remove the item from the array.
	if (index > -1)
	{
		if (index == (this->count - 1))
		{
			// the item was the lasst item in the array, don't really have to do anything.
		}
		else
		{

			// Shift all the items to the right of the item being removed 1 spot the left using
			// memcopy
			memcpy(
				this->internalList + (sizeof(void*) * (index)),  
				this->internalList + (sizeof(void*) * (index + 1)), 
				(sizeof(void*) * this->count - (index + 1)));
		}

		// reduce the count of the number of items in the list
		this->count = 
			this->count - 1;

		// Check if the percentage of the list buffer filled is below a certain threshold, then reduce the 
		// size of the buffer.
		int percentageFilled =
			(this->count * 100) / this->size;

		if (percentageFilled < _LIST_SHRINK_THRESHOLD_)
		{
			int newSize = _GROW_BY_PERCENT(this->count, _LIST_GROW_PERCENT_);

			if (newSize < this->count)
			{
				this->internalList = (void**)realloc(this->internalList, newSize * sizeof(void*));

				// Set the new size of the list.
				this->size = newSize;
			}
		}
	}
}

int List::Count()
{
	return this->count;
}

void* List::GetItem(int index)
{
	return this->internalList[index];
}

void List::Clear()
{
	free(this->internalList);

	this->size = _MAX_GROW_SIZE;
	this->count = 0;

	this->internalList = (void**)malloc(sizeof(void*) * size);
}

void List::FreeThenClear()
{
	for(int i = 0; i < this->Count(); i ++)
	{
		free(this->GetItem(i));
	}

	this->Clear();
}

void** List::ToArray()
{
	void** arr = (void**)malloc(sizeof(void*) * this->Count());

/*

	if (arr != NULL)
	{
		memcpy_s(arr, this->Count(), this->internalList, this->Count());
	}*/

	if (arr != NULL)
	{
		for(int i = 0; i < this->Count(); i ++)
		{
			arr[i] = this->GetItem(i);
		}
	}

	return arr;
}